/**
 * Copyright 2022 AntGroup CO., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 */

#include "lgraph/olap_on_db.h"
#include "tools/json.hpp"


using namespace lgraph_api;
using namespace lgraph_api::olap;
using json = nlohmann::json;


size_t cn_count_common(AdjList<Empty> & list_a, AdjList<Empty> & list_b) {
    size_t local_count = 0;
    AdjUnit<Empty> * ptr_a = list_a.begin();
    AdjUnit<Empty> * ptr_b = list_b.begin();
    while (ptr_a != list_a.end() && ptr_b != list_b.end()) {
        if (ptr_a->neighbour < ptr_b->neighbour) {
          ptr_a++;
        } else if (ptr_a->neighbour > ptr_b->neighbour) {
          ptr_b++;
        } else {
          local_count++;
          ptr_a++;
          ptr_b++;
        }
    }
    return local_count;
}

int cn_compare(const void * a, const void * b) {
    const AdjUnit<Empty> * ptr_a = (const AdjUnit<Empty>*) a;
    const AdjUnit<Empty> * ptr_b = (const AdjUnit<Empty>*) b;
    return (ptr_a->neighbour - ptr_b->neighbour);
}

size_t CNCore(OlapBase<Empty> & graph, std::pair<size_t, size_t> search_pair) {
    size_t src = search_pair.first;
    size_t dst = search_pair.second;

    qsort(graph.OutEdges(src).begin(), graph.OutDegree(src), sizeof(AdjUnit<Empty>), cn_compare);
    qsort(graph.OutEdges(dst).begin(), graph.OutDegree(dst), sizeof(AdjUnit<Empty>), cn_compare);

    AdjList<Empty> src_list = graph.OutEdges(src);
    AdjList<Empty> dst_list = graph.OutEdges(dst);

    return cn_count_common(src_list, dst_list);
}



extern "C" bool Process(GraphDB& db, const std::string& request, std::string& response) {
    auto start_time = get_time();

    // prepare
    start_time = get_time();
    std::string src_label = "node";
    std::string src_field = "id";
    std::string dst_label = "node";
    std::string dst_field = "id";
    std::vector<std::pair<size_t, size_t> > search_list
        = {};
    auto txn = db.CreateReadTxn();
    try {
        json input = json::parse(request);
        // parse_from_json(src_label, "src_label", input);
        // parse_from_json(src_field, "src_field", input);
        // parse_from_json(dst_label, "dst_label", input);
        // parse_from_json(dst_field, "dst_field", input);
        // if (input["search_pairs"].is_array()) {
        //     search_list.clear();
        //     for (auto &e : input["search_pairs"]) {
        //         int64_t src_id = e[0];
        //         int64_t dst_id = e[1];
        //         lgraph_api::FieldData src_field_data(src_id);
        //         lgraph_api::FieldData dst_field_data(dst_id);
        //         size_t src = txn.GetVertexIndexIterator(src_label, src_field,
        //                                 src_field_data, src_field_data).GetVid();
        //         size_t dst = txn.GetVertexIndexIterator(dst_label, dst_field,
        //                                 dst_field_data, dst_field_data).GetVid();
        //         search_list.push_back(std::make_pair(src, dst));
        //     }
        // }
    } catch (std::exception &e) {
        throw std::runtime_error("json parse error");
    }

    OlapOnDB<Empty> olapondb(db, txn, SNAPSHOT_PARALLEL | SNAPSHOT_UNDIRECTED);
    auto prepare_cost = get_time() - start_time;

    // core
    start_time = get_time();

    size_t vid = 0;


    //获取顶点0的所有相邻边
    for (auto & edge : olapondb.OutEdges(vid)) {
        size_t dst = edge.neighbour;
        std::pair<size_t,size_t> search_pair = {vid,dst};
        search_list.push_back(search_pair);
        
    }

    //std::vector< std::tuple<size_t, size_t, double> > result_list;
    size_t origin = 0;
    size_t total = 0;
    for (auto search_pair : search_list) {
        size_t count_common = CNCore(olapondb, search_pair);
        total = total + count_common;
        //result_list.push_back(std::make_tuple(search_pair.first, search_pair.second, count_common));
    }
    auto core_cost = get_time() - start_time;


    //去重
    origin = total;
    total = total /2;

    // return
    json output;
    //output["cn_list"] = result_list;
    output["num_vertices"] = olapondb.NumVertices();
    output["num_edges"] = olapondb.NumEdges();
    output["origin"] = origin;
    output["total"] = total;
    //output["prepare_cost"] = prepare_cost;
    //output["core_cost"] = core_cost;
    //output["total_cost"] = prepare_cost + core_cost;
    response = output.dump();
    return true;
}
